package com.yyfax.account.controller.api;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.jaws.core.common.exception.JawsCommonExceptionCode;
import com.jaws.core.common.log.Log;
import com.jaws.core.common.validator.JawsValidator;
import com.jaws.core.common.vo.OpRequest;
import com.jaws.core.common.vo.OpResponse;
import com.yyfax.account.common.enums.TradeTypeEnum;
import com.yyfax.account.common.exception.AccountExceptionCode;
import com.yyfax.account.service.AccountInfoService;
import com.yyfax.account.service.AccountTradeFactory;
import com.yyfax.account.service.AccountTradeService;
import com.yyfax.account.vo.AccountInfoVO;
import com.yyfax.account.vo.BaseTradeReq;
import com.yyfax.account.vo.BatchOpenAccountReq;
import com.yyfax.account.vo.BatchTradeReq;
import com.yyfax.account.vo.FrozenReq;
import com.yyfax.account.vo.OpenAccountReq;
import com.yyfax.account.vo.TradeReq;
import com.yyfax.account.vo.UnFrozenReq;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import java.util.List;
import java.util.Set;

/**
 * 账户API
 *
 * @author maojian
 * @date 2017-05-23 16:02:06
 * @since v1.0.0
 */
@Api(description = "账户API")
@RestController
public class AccountApiController {

	private Logger logger = LoggerFactory.getLogger(getClass());

	@Resource
	private AccountInfoService accountInfoService;

	@Resource
	private AccountTradeService accountTradeService;

	@Resource
	private AccountTradeFactory accountTradeFactory;

	@Resource
	private JawsValidator validator;

	@Value("${BATCH_TRADE_SIZE:100}")
	private Integer batchTradeSize;

	@Value("${BATCH_SETTLE_SIZE:100}")
	private Integer batchSettleSize;

	/**
	 * 开户
	 *
	 * @param req
	 * @return
	 * @throws Throwable
	 * @author maojian
	 * @date 2017-05-3 09:04:14
	 * @since v1.0.0
	 */
	@ApiOperation("开户")
	@RequestMapping(method = RequestMethod.POST, path = "/api/account/open")
	public OpResponse openAccount(@RequestBody @Valid OpRequest<OpenAccountReq> req) throws Exception {
		logger.info(Log.op("AccountInfoApiController.openAccount").msg("enter").kv("req", req).toString());

		OpResponse response = OpResponse.success();
		AccountInfoVO vo = accountInfoService.openAccount(req.getParams());
		response.setContent(vo.getAccountNo());
		return response;
	}

	/**
	 * 批量开户
	 *
	 * @param req
	 * @return
	 * @throws Exception
	 * @author buyi
	 * @date 2017-09-26 11:12:48
	 * @since v2.0.0
	 */
	@ApiOperation("批量开户")
	@RequestMapping(method = RequestMethod.POST, path = "/api/account/batch/open")
	public OpResponse openAccountBatch(@RequestBody @Valid OpRequest<BatchOpenAccountReq> req) throws Exception {
		logger.info(Log.op("AccountInfoApiController.openAccountBatch").msg("enter").kv("req", req).toString());

		OpResponse response = OpResponse.success();
		response.setContent(accountInfoService.openAccountBatch(req.getParams()));
		return response;
	}

	/**
	 * 账户交易操作
	 *
	 * @param req
	 * @return
	 * @throws Exception
	 * @author maojian
	 * @date 2017-10-24 14:45:37
	 * @since v1.0.0
	 */
	@RequestMapping(method = RequestMethod.POST, path = "/api/account/trade")
	public OpResponse trade(@RequestBody @Valid OpRequest<TradeReq> req) throws Exception {
		logger.info(Log.op("AccountInfoApiController.trade").msg("enter").kv("req", req).toString());
		accountTradeService.trade(req.getParams());
		return OpResponse.success();
	}


	/**
	 * 账户批量操作
	 *
	 * @param req
	 * @return
	 * @throws Throwable
	 * @author maojian
	 * @date 2017-05-3 09:04:14
	 * @since v1.0.0
	 */
	@SuppressWarnings("rawtypes")
	@RequestMapping(method = RequestMethod.POST, path = "/api/account/batch/operate")
	public OpResponse batchOperate(@RequestBody @Valid OpRequest<BatchTradeReq> req) throws Exception {
		logger.info(Log.op("AccountInfoApiController.batchOperate").msg("enter").kv("req", req).toString());

		//校验批量数据量
		checkRequestTradeSize(req);

		BatchTradeReq batchTradeReq = convertJsonToTrade(req);
		accountTradeService.batchTrade(batchTradeReq);
		return OpResponse.success();
	}


	/**
	 * 账户批量结算
	 *
	 * @param req
	 * @return
	 * @throws Throwable
	 * @author liangqm
	 * @date 2017-11-09 09:04:14
	 * @since v1.0.0
	 */
	@SuppressWarnings("rawtypes")
	@RequestMapping(method = RequestMethod.POST, path = "/api/account/batch/settle")
	public OpResponse batchSettle(@RequestBody @Valid OpRequest<BatchTradeReq> req) throws Exception {
		logger.info(Log.op("AccountInfoApiController.batchSettle").msg("enter").kv("req", req).toString());

		//校验批量数据量
		checkRequestSettleSize(req);

		BatchTradeReq batchTradeReq = convertJsonToTrade(req);
		OpResponse response = OpResponse.success();
		response.setContent(accountTradeService.batchSettle(batchTradeReq));
		return response;
	}

	/**
	 * 检查批量交易数量
	 *
	 * @param req
	 * @return
	 * @author liangqm
	 * @date 2017-12-04 09:04:14
	 * @since v1.0.0
	 */
	private void checkRequestTradeSize(OpRequest<BatchTradeReq> req) {
		List<BaseTradeReq> tradeLs = req.getParams().getTradeLs();
		if (CollectionUtils.isNotEmpty(tradeLs)) {
			if (tradeLs.size() > batchTradeSize) {
				throw AccountExceptionCode.BATCH_TRADE_SIZE_LIMIT.exp(tradeLs.size(), batchTradeSize);
			}
		}
	}

	/**
	 * 检查批量结算数量
	 *
	 * @param req
	 * @return
	 * @author liangqm
	 * @date 2017-12-04 09:04:14
	 * @since v1.0.0
	 */
	private void checkRequestSettleSize(OpRequest<BatchTradeReq> req) {
		List<BaseTradeReq> tradeLs = req.getParams().getTradeLs();
		if (CollectionUtils.isNotEmpty(tradeLs)) {
			if (tradeLs.size() > batchSettleSize) {
				throw AccountExceptionCode.BATCH_SETTLE_SIZE_LIMIT.exp(tradeLs.size(), batchSettleSize);
			}
		}
	}

	/**
	 * 批量对象json转换成java对象，验签和参数校验（序列化传输不用写这个）
	 *
	 * @param req
	 * @return
	 * @author maojian
	 * @date 2017-05-27 16:30:57
	 * @since v1.0.0
	 */
	@SuppressWarnings({"rawtypes", "unchecked"})
	private BatchTradeReq convertJsonToTrade(OpRequest<BatchTradeReq> req) {
		BatchTradeReq batchReq = new BatchTradeReq();
		List<BaseTradeReq> tradeReqLs = Lists.newArrayList();
		List<JSONObject> reqLs = req.getParams().getTradeLs();
		for (JSONObject json : reqLs) {
			BaseTradeReq trade = accountTradeFactory.covertToTradeReq(TradeTypeEnum.valueOf(json.getString("tradeType")), json);
			Set<ConstraintViolation<BaseTradeReq>> set = validator.getValidator().validate(trade);
			if (CollectionUtils.isNotEmpty(set)) {
				ConstraintViolation<BaseTradeReq> cv = set.iterator().next();
				throw JawsCommonExceptionCode.PARAM_ERROR.exp(cv.getMessage());
			}
			tradeReqLs.add(trade);
		}
		batchReq.setTradeLs(tradeReqLs);
		return batchReq;
	}

	/**
	 * 批量综合交易</br>
	 * 账户同时操作余额和冻结余额
	 *
	 * @param req 批量综合交易请求参数
	 * @return OpResponse 返回参数
	 * @author liangqm
	 * @date 2018-01-15 09:04:18
	 * @since v2.0.3
	 */
	@RequestMapping(method = RequestMethod.POST, path = "/api/account/batch/complex/operate")
	public OpResponse batchComplexTrade(@RequestBody @Valid OpRequest<BatchTradeReq> req) {

		//检查参数数量
		checkRequestTradeSize(req);
		BatchTradeReq batchTradeReq = convertJsonToTrade(req);

		OpResponse response = OpResponse.success();
		accountTradeService.batchComplexTrade(batchTradeReq);
		return response;
	}
}
